modprobe

modprobe 这个命令是用来往内核中加载(或者移除)模块用的,我很奇怪加载不应该是 insmod 的么?然后查了一下,他们是有区别的,虽然都可以用来往内网中加载模块:

如果 modprobe 在加载过程中发生错误,会卸载整组的模块。modprobe 寻找模块的位置是:/lib/modules/<uname -r> ,但是不会加载 /etc/modprobe.conf/etc/modprobe.d/ 中配置的排除内容。

当然,modprobe 的依赖分析不是天马行空来的,而是通过 depmod 生成的。

动态库的查找过程

任何一个动态链接的模块所依赖的模块路径保存在 .dynamic 段里面,由 DT_NEED 类型的项表示。动态链接器对于模块的查找有一定的规则:

ld.so.conf 是一个文本配置文件,它可能包含其他的配置文件,这些配置文件中存放着目录信息。如果动态链接器在每次查找共享库时都去遍历这些目录,那将会非常耗时。所以 Linux 系统中都有一个叫做 ldconfig 的程序,这个程序的作用是为共享库目录下的各个共享库创建、删除或更新相应的 SO_NAME,这样每个共享库的 SO-NAME 就能够指向正确地共享库文件;并且这个程序还会将这些 SO-NAME 收集起来,集中存放到 /etc/ld.so.cache 文件里面,并建立一个 SO-NAME 的缓存。当动态链接库要查找共享库时,它可以直接从 /etc/ld.so.cache 里面查找,而 /etc/ld.so.cache 的结构是经过特殊设计的,非常适合查找,所以这个设计大大加快了共享库的查找过程。

如果动态链接器在 /etc/ld.so.cache 里面没有找到所需要的共享库,那么它还会遍历 /lib/usr/lib 这两个目录,如果还是没找到,就宣告失败。

所以理论上讲,如果我们在系统指定的共享库目录下添加、删除或者更新任何一个共享库,或者我们更新了 /etc/ld.so.conf 的配置,都应该运行 ldconfig 这个程序,以便调整 SO-NAME/etc/ld.so.cache。很多软件包的安装程序在往系统里面安装共享库以后都会调用 ldconfig

ldconfig

ldconfig 这个命令的作用就是在指定的位置查找动态链接库,然后加载进系统的内存(内核空间还是用户空间?没有深究)。所以,一般这个命令是在系统启动的时候运行的,这样,在我们升级或者自己编译新的动态链接库之后,就需要自己手动执行一遍 ldconfig 才能让新的动态链接库生效。

环境变量

LD_LIBRARY_PATH

在 Linux 系统中,LD_LIBRARY_PATH 是一个由若干个路径组成的环境变量,每个路径之间由冒号隔开。默认情况下,LD_LIBRARY_PATH 为空。如果我们为某个进程设置了 LD_LIBRARY_PATH,那么进程在启动时,动态链接器在查找共享库时,会首先查找由 LD_LIBRARY_PATH 指定的目录。这个环境可以很方便地让我们测试新的共享库或使用非标准的共享库。

示例:LD_LIBRARY_PATH=/home/liqiang.io /bin/ls

当然,还有另外一个等价的使用方式:

  1. [root@liqiang.io]# /lib/ld-linux.so.2 -library-path /home/liqiang.io /bin/ls

所以,当引入 LD_LIBRARY_PATH 之后,我们总结动态链接器查找共享库的顺序为:

  1. 由环境变量 LD_LIBRARY_PATH 指定的路径;
  2. 由路径缓存文件 /etc/ld.so.cache 指定的路径;
  3. 默认共享库目录,先 /usr/lib,再 /lib
LD_PRELOAD

系统还存在另外一个环境变量叫做 LD_PRELOAD,这个文件中我们可以指定预先装载的一些共享库或者目标文件。在 LD_PRELOAD 里面指定的文件会在动态链接器按照固定规则搜索共享库之前装载,它比 LD_LIBRARY_PATH 里面所指定的目录中的共享库还要优先。无论程序是否依赖于它们,LD_LIBRARY_PATH 里面指定的共享库或目标文件都会被装载。

由于全局符号介入这个机制的存在,LD_PRELOAD 里面指定的共享库或目标文件中的全局符号就会覆盖后面加载的同名全局符号,这使得我们可以很方便地做到改写标准 C 库中的某个或某几个函数而不影响其他函数,对于程序的调试或测试非常有用。

/etc/ld.so.preload)等效于这个环境变量。

LD_DEBUG

还有一个非常有用的环境变量是 LD_DEBUG,这个变量可以打开动态链接器的调试功能,当我们设置这个变量时,动态链接器会在运行时打印出各种有用的信息,对于我们开发和调试共享库有很大的帮助。

LD_DEBUG 的可选值有:

Ref